package cn.ysbd.datafactory.utils;

import cn.ysbd.datafactory.config.JedisPoolFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @Author: YinHeng
 * @Description:
 * @DateTime: 2022/8/31 17:15
 */
public class RedisLock {

    private final static Logger LOGGER = LoggerFactory.getLogger(RedisLock.class.getName());

    private String threadName = Thread.currentThread().getName();

    private Long threadId = Thread.currentThread().getId();

    private String lock_key = "lock_key";


    /**
     * static变量，final修饰，加载在内存中，JVM进程生命周期中不变
     */
    private static final String APP_ID = UUID.randomUUID().toString();

    String releaseLock_lua = "if redis.call(\"get\",KEYS[1]) == ARGV[1] \n" +
            "then\n" +
            "    return redis.call(\"del\", KEYS[1])\n" +
            "else\n" +
            "    return 0\n" +
            "end";
    String addLockLife_lua = "if redis.call(\"exists\", KEYS[1]) == 1\n" +
            "then\n" +
            "    return redis.call(\"expire\", KEYS[1], ARGV[1])\n" +
            "else\n" +
            "    return 0\n" +
            "end";

    public void lock() {
        // 判断是否可重入
        if (isHeldByCurrentThread()) {
            return;
        }
        // 自旋
        while (!tryLock()) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        LOGGER.debug("线程：" + threadName + "，占锁成功！★★★");
    }

    /**
     * 是否是当前线程占有锁，同时将超时时间重新设置，这个很重要，同样也是原子操作
     */
    private boolean isHeldByCurrentThread() {

        List<byte[]> keys = Arrays.asList(encode(lock_key));
        List<byte[]> args = Arrays.asList(encode(APP_ID + String.valueOf(threadId)), encode(String.valueOf(1)));
        Jedis jedis = JedisPoolFactory.getJedis();
        try {
            long eval = (Long) jedis.eval(encode(addLockLife_lua), keys, args);
            return eval == 1;
        } finally {
            jedis.close();
        }


    }

    private boolean tryLock() {
        SetParams setParams = new SetParams();
        // 超时时间1s
        setParams.ex(10);
        setParams.nx();
        // 转换为redis命令就是：set haolin-key xxx ex 1 nx
        Jedis jedis = JedisPoolFactory.getJedis();
        try {
            String response = jedis.set(lock_key, APP_ID + String.valueOf(threadId), setParams);
            return "OK".equals(response);
        } finally {
            jedis.close();
        }

    }

    public void unlock() {

        List<byte[]> keys = Arrays.asList(encode(lock_key));
        List<byte[]> args = Arrays.asList(encode(APP_ID + String.valueOf(threadId)));

        // 使用lua脚本，保证原子性
        Jedis jedis = JedisPoolFactory.getJedis();
        try {
            long eval = (Long) jedis.eval(encode(releaseLock_lua), keys, args);
            if (eval == 1) {
                LOGGER.debug("线程：" + threadName + " 释放锁成功！☆☆☆");
            } else {
               LOGGER.debug("线程：" + threadName + " 释放锁失败！该线程未持有锁！！！");
            }
        } finally {
            jedis.close();
        }
    }

    private byte[] encode(String param) {
        return param.getBytes();
    }
}
